#include <Scene2D.h>
#include <ALEstd.h>
#include <D3D9Renderer.h>
#include <FastDelegate.h>
#include <CoreEvents.h>

Mat4x4 Camera2D::GetWorldView(Scene2D * pScene)
{
	const Mat4x4* world = pScene->GetTopMatrix();
	Mat4x4 view;
	D3DXMatrixTransformation2D(&view, NULL, 0, NULL, NULL, 0, &(-m_area.GetPosition()));
	//Mat4x4 worldview = (*world) * m_props.FromWorld();
	Mat4x4 worldview = (*world) * view;
	return worldview;
}

RootNode2D::RootNode2D()
	: SceneNode2D("Root", &Vec2(0,0), &Vec2(1,1))
{
	//todo render groups
}

bool RootNode2D::VAddChild(std::shared_ptr<ISceneNode2D> kid)
{
	//todo render groups
	return SceneNode2D::VAddChild(kid);
}

bool RootNode2D::VRemoveChild(ActorId id)
{
	//todo render groups
	return SceneNode2D::VRemoveChild(id);
}

HRESULT RootNode2D::VRenderChildren(Scene2D *pScene)
{
	g_pRenderer->GetSpriteHandler()->Begin(D3DXSPRITE_ALPHABLEND);
	HRESULT hr = SceneNode2D::VRenderChildren(pScene);
	g_pRenderer->GetSpriteHandler()->End();

	return hr;
}

SpriteNode::SpriteNode(const TCHAR* file, const Vec2 *pos, const Vec2 *size, double orientation, ActorId id)
		: SceneNode2D("Sprite", pos, size, orientation, id)
{
	m_filename = file;
	m_pTexture = NULL;
}


HRESULT SpriteNode::VRestore(Scene2D* pScene)
{
	if (!m_pTexture)
	{
		HRESULT result;
		result = D3DXGetImageInfoFromFile(m_filename, &info);
		if (result != D3D_OK) 	{
			m_pTexture = NULL;
			return result;
		}

		D3DCOLOR transcolor = 0xffff00ff;
		//create the new CTexture by loading a bitmap image file
		result = D3DXCreateTextureFromFileEx( 
			g_pRenderer->GetDevice(), //Direct3D device object
			m_filename,      //bitmap filename
			info.Width,            //bitmap image width
			info.Height,           //bitmap image height
			1,                     //mip-map levels (1 for no chain)
			D3DPOOL_DEFAULT,       //the type of surface (standard)
			D3DFMT_UNKNOWN,        //surface format (default)
			D3DPOOL_DEFAULT,       //memory class for the CTexture
			D3DX_DEFAULT,          //image filter
			D3DX_DEFAULT,          //mip filter
			transcolor,            //color key for transparency
			&info,                 //bitmap file info (from loaded file)
			NULL,                  //color palette
			&m_pTexture );            //destination CTexture

		//make sure the bitmap textre was loaded correctly
		if (result != D3D_OK) 	{
			m_pTexture = NULL;
			return result;
		}

		UpdateScale();
	}

	return S_OK;
}

void SpriteNode::UpdateScale()
{
	if (m_pTexture)
	{
		m_scale = Vec2(m_props.Size().x/info.Width, m_props.Size().y/info.Height);
	}
}

SpriteNode::~SpriteNode()
{
	SAFE_RELEASE(m_pTexture);
}

bool SpriteNode::VIsVisible(Scene2D* pScene)
{
	return true;
}

HRESULT SpriteNode::VRender(Scene2D* pScene)
{
	if(m_pTexture)
	{
		RECT srcRect;
		srcRect.left = 0;
		srcRect.right = info.Width;
		srcRect.top = 0;
		srcRect.bottom = info.Height;

		g_pRenderer->GetSpriteHandler()->SetTransform(&pScene->GetCamera()->GetWorldView(pScene));
		g_pRenderer->GetSpriteHandler()->Draw(m_pTexture, &srcRect, NULL, NULL, m_props.Color());
		g_pRenderer->GetSpriteHandler()->SetTransform(pScene->GetTopMatrix());
	}
	return S_OK;
}

Scene2D::Scene2D()
{
	m_pRoot.reset(ALE_NEW RootNode2D());
	D3DXCreateMatrixStack(0, &m_pMatrixStack);
	RegisterEvents();
	m_bVisible = true;
	m_zOrder = 1;
}

Scene2D::~Scene2D()
{
	SAFE_RELEASE(m_pMatrixStack);
}

HRESULT Scene2D::VRender()
{
	if (m_pRoot && m_pCamera)
	{
		//TODO: camera transform
		if (m_pRoot->VPreRender(this) == S_OK)
		{
			m_pRoot->VRender(this);
			m_pRoot->VRenderChildren(this);
			m_pRoot->VPostRender(this);

			return S_OK;
		}
	}
	return E_FAIL;
}

HRESULT Scene2D::VRestore()
{
	if (!m_pRoot)
		return E_FAIL;

	return m_pRoot->VRestore(this);
}

void Scene2D::VUpdate(double deltaSecs)
{
	if(m_pRoot)
		m_pRoot->VUpdate(this, deltaSecs);
}

bool Scene2D::AddChild(std::shared_ptr<ISceneNode2D> kid, ActorId id)
{
	if (id != 0)
	{
		m_actorMap[id] = kid;
	}
	return m_pRoot->VAddChild(kid);
}

bool Scene2D::RemoveChild(ActorId id)
{
	m_actorMap.erase(id);
	return m_pRoot->VRemoveChild(id);
}

std::shared_ptr<ISceneNode2D> Scene2D::FindActor(ActorId id)
{
	auto i = m_actorMap.find(id);
	if (i==m_actorMap.end())
	{
		std::shared_ptr<ISceneNode2D> null;
		return null;
	}

	return (*i).second;
}

void Scene2D::PushAndSetMatrix(const Mat4x4 &toWorld)
{
	m_pMatrixStack->Push();
	m_pMatrixStack->MultMatrixLocal(&toWorld);
	g_pRenderer->GetSpriteHandler()->SetTransform(m_pMatrixStack->GetTop());
}

void Scene2D::PopMatrix()
{
	m_pMatrixStack->Pop(); 
	g_pRenderer->GetSpriteHandler()->SetTransform(m_pMatrixStack->GetTop());	
}

using fastdelegate::MakeDelegate;
void Scene2D::RegisterEvents()
{
	IEventManager* pEventMgr = IEventManager::Get();
	pEventMgr->VAddListener(MakeDelegate(this, &Scene2D::OnNewRenderComponent), Event_New_RenderComponent_2D::sk_eventType);
	pEventMgr->VAddListener(MakeDelegate(this, &Scene2D::OnMoveActor), Event_Move_Actor_2D::sk_eventType);
}

void Scene2D::OnNewRenderComponent(IEventPtr pEvent)
{
	std::shared_ptr<Event_New_RenderComponent_2D> pCastEvent = std::static_pointer_cast<Event_New_RenderComponent_2D>(pEvent);

	ActorId id = pCastEvent->m_id;
	std::shared_ptr<ISceneNode2D> pNode = pCastEvent->m_sceneNode;
	if (FAILED(pNode->VRestore(this)))
		return;

	AddChild(pNode, id);
}

void Scene2D::OnMoveActor(IEventPtr pEvent)
{
	std::shared_ptr<Event_Move_Actor_2D> castEvent = std::static_pointer_cast<Event_Move_Actor_2D>(pEvent);

	ActorId id = castEvent->m_id;
	std::shared_ptr<ISceneNode2D> pNode = FindActor(id);
	Vec2 pos = castEvent->m_position + pNode->VGet()->Size()/2;
	double ori = castEvent->m_orientation;
	Vec2 size = castEvent->m_size;
	pNode->VSetPosition(pos);
	pNode->VSetOrientation(ori);
	pNode->VSetSize(size);
}